﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XCommunity.Caching
{

  /// <summary>
  /// 缓存项，缓存服务对缓存值的包装，用于对缓存值的管理
  /// </summary>
  public class CacheItem
  {

    private object _key;
    private object _value;
    private DateTime _addTime;
    private DateTime _refreshTime;
    private CacheContainer _cacheContainer;

    private ICacheDependency _cacheDenpendency;

    /// <summary>
    /// 创建一个缓存项
    /// </summary>
    /// <param name="key">索引键</param>
    /// <param name="value">缓存值</param>
    public CacheItem( object key, object value )
      : this( key, value, null ) { }


    public CacheItem( object key, object value, ICacheDependency cacheDependency )
    {

      _cacheDenpendency = cacheDependency;

      if ( key == null )
        throw new ArgumentNullException( "key" );

      if ( value == null )
        throw new ArgumentNullException( "value" );

      _key = key;
      _value = value;

      InitializeValue();
    }


    private void InitializeValue()
    {
      _writeBackValue = Value as IWriteBackCacheValue;
      _notifyStatusCacheValue = Value as INotifyStatusCacheValue;
    }


    internal void AddTo( CacheContainer cache )
    {
      _cacheContainer = cache;

      _identity = new CacheIdentiity( CacheContainer, Key );

      _addTime = _refreshTime = _cacheContainer.Service.GetTime();

      _addedToCache = true;

      ICacheControllerValue cacheController = _value as ICacheControllerValue;
      if ( cacheController != null )
        cacheController.AddedToCache( this );
    }

    /// <summary>
    /// 缓存项的索引键
    /// </summary>
    public object Key
    {
      get { return _key; }
    }

    /// <summary>
    /// 缓存项所缓存的值
    /// </summary>
    protected object Value
    {
      get { return _value; }
    }

    /// <summary>
    /// 缓存项所在的缓存容器
    /// </summary>
    public CacheContainer CacheContainer
    {
      get { return _cacheContainer; }
    }

    /// <summary>
    /// 管理缓存项的缓存服务
    /// </summary>
    public CacheService Service
    {
      get { return CacheContainer.Service; }
    }

    /// <summary>
    /// 重新设置缓存值
    /// </summary>
    /// <param name="value">新的缓存值</param>
    public virtual void SetValue( object value )
    {
      if ( value == null )
        throw new ArgumentNullException( "value" );

      _value = value;
      _refreshTime = _cacheContainer.Service.GetTime();

      InitializeValue();
    }

    /// <summary>
    /// 尝试获取缓存值，若缓存项已经失效，则返回null
    /// </summary>
    /// <returns></returns>
    public virtual object TryGetValue()
    {
      if ( Available )
      {
        _hits++;
        return Value;
      }
      else
        return null;
    }


    private int _hits = 0;
    internal int Hits
    {
      get { return _hits; }
    }

    /// <summary>
    /// 缓存项被创建的时间
    /// </summary>
    public DateTime AddTime
    {
      get { return _addTime; }
    }

    /// <summary>
    /// 缓存项最后一次被刷新的时间
    /// </summary>
    public DateTime RefreshTime
    {
      get
      {

        if ( _notifyStatusCacheValue != null )
        {
          lock ( _notifyStatusCacheValue )
          {
            return _notifyStatusCacheValue.RefreshTime ?? _refreshTime;
          }
        }

        return _refreshTime;
      }
    }

    /// <summary>
    /// 请求检查缓存项是否仍然有效
    /// </summary>
    /// <returns></returns>
    public virtual bool CheckAvailable()
    {

      if ( _notifyStatusCacheValue != null )
      {
        lock ( _notifyStatusCacheValue )
        {
          if ( _notifyStatusCacheValue.Available != null )
            return (bool) _notifyStatusCacheValue.Available;
        }
      }


      if ( CacheContainer == null )
        throw new InvalidOperationException();

      if ( Available )
        SetAvailable( CacheContainer.CheckAvailable( this ) );
      return Available;
    }

    private bool _available = true;

    /// <summary>
    /// 指示缓存项是否有效
    /// </summary>
    public virtual bool Available
    {
      get
      {

        if ( !_addedToCache )
          throw new InvalidOperationException();

        if ( _notifyStatusCacheValue != null )
        {
          lock ( _notifyStatusCacheValue )
          {
            return _notifyStatusCacheValue.Available ?? _available;
          }
        }

        return _available;
      }
    }

    /// <summary>
    /// 设置缓存项的有效状态
    /// </summary>
    /// <param name="value"></param>
    protected virtual void SetAvailable( bool value )
    {
      if ( !_addedToCache )
        throw new InvalidOperationException();//UNDONE 详细的异常信息

      if ( _notifyStatusCacheValue != null )
        throw new InvalidOperationException();//UNDONE 详细的异常信息

      if ( value == _available )
        return;

      _available = value;
    }

    private bool _addedToCache = false;


    /// <summary>
    /// 通知CacheItem，缓存的值已经被刷新。
    /// </summary>
    public virtual void ValueRefreshed()
    {
      _refreshTime = Service.GetTime();
    }


    private IWriteBackCacheValue _writeBackValue;

    internal bool IsWriteBackValue
    {
      get { return _writeBackValue != null; }
    }

    internal IWriteBackCacheValue WriteBackValue
    {
      get { return _writeBackValue; }
    }

    private INotifyStatusCacheValue _notifyStatusCacheValue;


    /// <summary>
    /// 尝试将写缓存项回写
    /// </summary>
    /// <returns>是否已经被回写</returns>
    public virtual bool TryWriteBack()
    {
      if ( IsWriteBackValue )
      {
        return WriteBack( false );
      }

      return false;
    }

    internal bool WriteBack( bool ignoreAvailable )
    {
      if ( ( Available || ignoreAvailable ) && WriteBackValue != null )
        return WriteBackValue.WriteBack();
      else
        return false;
    }


    private CacheIdentiity _identity;
    private Guid _guid = Guid.NewGuid();

    private object Identity
    {
      get { return _guid; }
    }

    public override bool Equals( object obj )
    {
      CacheItem item = obj as CacheItem;

      if ( item == null || item.Identity == null || this.Identity == null )
        return false;

      return item.Identity.Equals( this.Identity );
    }

    public override int GetHashCode()
    {
      if ( Identity == null )
        return base.GetHashCode();
      return Identity.GetHashCode();
    }

    private class CacheIdentiity
    {

      private CacheContainer cache;
      private object key;

      public CacheIdentiity( CacheContainer cacheContainer, object key )
      {
        this.cache = cacheContainer;
        this.key = key;
      }

      public override int GetHashCode()
      {
        return cache.GetHashCode() ^ key.GetHashCode();
      }

      public override bool Equals( object obj )
      {
        CacheIdentiity identity = obj as CacheIdentiity;
        if ( identity == null )
          return false;

        return identity.cache.Equals( cache ) && identity.key.Equals( key );
      }
    }
  }
}
